import { h } from "koishi"

export const name = "-lnnbot-anti-weird-chars"
export const inject = { optional: ["database"] }

const homoglyphMap = {
  // 希腊字母
  Α: 'A', Β: 'B', Ε: 'E', Ζ: 'Z',
  Η: 'H', Ι: 'I', Κ: 'K', Μ: 'M',
  Ν: 'N', Ο: 'O', Ρ: 'P', Τ: 'T',
  Υ: 'Y', Χ: 'X', ο: 'o', ϳ: 'j',
  // 西里尔字母
  А: 'A', В: 'B', С: 'C', Е: 'E',
  І: 'I', К: 'K', М: 'M', Н: 'H',
  О: 'O', Р: 'P', Т: 'T', Ү: 'Y',
  Х: 'X', а: 'a', с: 'c', е: 'e',
  і: 'i', о: 'o', р: 'p', у: 'y',
  х: 'x', Ԛ: 'Q', Ԝ: 'W', һ: 'h',
  ԛ: 'q', ԝ: 'w',
  // 罗马数字
  Ⅽ: 'C', Ⅾ: 'D', Ⅰ: 'I', Ⅿ: 'M',
  Ⅴ: 'V', Ⅹ: 'X',
  // 其他
  K: 'K', ɑ: 'a', ɡ: 'g',
}

const zeroWidthChars = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\u200B-\u200D\u202A-\u202E\u2060\u2066-\u2069\uFEFF\uFFF9-\uFFFB]/

function normalize(str) {
  const weirdChars = []
  let newStr = ""
  for (let ch of str) {
    if (zeroWidthChars.test(ch)) {
      weirdChars.push(ch)
      continue
    }
    if (Object.hasOwn(homoglyphMap, ch)) {
      weirdChars.push(ch)
      ch = homoglyphMap[ch]
    }
    newStr += ch
  }
  return [newStr.toLowerCase().replaceAll("_", "-"), weirdChars]
}

async function complain(session, weirdChars) {
  return [
    h.quote(session.messageId),
    ...await session.execute({
      name: "uniname",
      args: [weirdChars.join("")],
    }, true),
  ]
}

export function apply(ctx) {
  ctx.middleware(async (session, next) => {
    if (session.stripped.prefix != null && !(session.stripped.hasAt && !session.stripped.atSelf)) {
      const name = session.stripped.content.slice(session.stripped.prefix.length).split(" ", 1)[0]
      const [normalizedName, weirdChars] = normalize(name)
      ctx.logger.debug(normalizedName)
      if (weirdChars.length && ctx.$commander.get(normalizedName, session))
        return complain(session, weirdChars)
      return next()
    }
    return next()
  })
  ctx.on("command/before-execute", async ({ session, command, args }) => {
    if (command.name === "help" && args[0]) {
      const [normalizedName, weirdChars] = normalize(args[0])
      if (weirdChars.length && ctx.$commander.get(normalizedName, session))
        return complain(session, weirdChars)
    } else if (command.name === "whatcmd" && args[0]) {
      const name = args[0] === "help" ? args[1] : args[0]
      const [normalizedName, weirdChars] = normalize(name)
      if (weirdChars.length && (await ctx.database?.get("whatcommands", normalizedName, ["name"])).length)
        return complain(session, weirdChars)
    }
  })
}
